/**
 *
 */
package com.fita.webpay.web.controller;

import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.alipay.api.internal.util.AlipaySignature;
import com.demo2do.core.entity.exception.BusinessException;
import com.demo2do.core.utils.DateFormatUtils;
import com.demo2do.core.utils.JsonUtils;
import com.demo2do.core.web.utils.WebUtils;
import com.demo2do.core.wechat.entity.pay.PayTransaction;
import com.demo2do.core.wechat.entity.response.Response;
import com.fita.webpay.entity.PaymentContext;
import com.fita.webpay.entity.callback.CallbackContext;
import com.fita.webpay.entity.callback.CallbackRequest;
import com.fita.webpay.entity.callback.CallbackResponse;
import com.fita.webpay.entity.callback.HandpayCallbackRequest;
import com.fita.webpay.entity.callback.WechatpayCallbackRequest;
import com.fita.webpay.handler.PaymentFacade;
import com.fita.webpay.service.PaymentService;
import com.yeepay.bc.app.ut.Base64;
import com.yeepay.g3.sdk.yop.encrypt.CertTypeEnum;
import com.yeepay.g3.sdk.yop.encrypt.DigitalEnvelopeDTO;
import com.yeepay.g3.sdk.yop.utils.DigitalEnvelopeUtils;
import com.yeepay.g3.sdk.yop.utils.InternalConfig;

/**
 * @author downpour
 *
 */
@Controller
@RequestMapping("")
public class PaymentCallbackListener {

    private static final Logger logger = LoggerFactory.getLogger(PaymentCallbackListener.class);

    @Autowired
    private PaymentFacade paymentFacade;

    @Autowired
    private PaymentService paymentService;
    
    @Value("#{payment['alipay.public.key']}") 
    private String alipayPublicKey;
    
    @Value("#{payment['alipay.seller.id']}") 
    private String alipaySellerId;
    
    /**
     * 
     * @param requestParameters
     * @return
     */
    @RequestMapping(value = "/wechatpay-callback", method = RequestMethod.POST, produces = "application/xml; charset=UTF-8")
    public @ResponseBody Response onWechatpayNotify(@RequestBody Map<String, String> requestParameters) {
    	
    	CallbackRequest callbackRequest = new WechatpayCallbackRequest(requestParameters);
        
    	CallbackResponse callbackResponse = new CallbackResponse();
    	
    	try {

    	    if(callbackRequest.isValid()) {
    	        
    	        // 0. extract pay_transaction from request
    	        PayTransaction payTransaction = callbackRequest.getPayTransaction();
    	        
    	        // 1. load payment_context by out_trade_no
                PaymentContext paymentContext =  paymentService.loadContext(payTransaction.getOutTradeNo());
                
                // 2. validate payment_context and callback_request, may throw business exception here
                paymentFacade.validate(paymentContext, callbackRequest);
                
                // 3. do paid process 
                callbackResponse = paymentFacade.paid(paymentContext, payTransaction);
    	        
    	    } else {
    	        
    	        callbackResponse.fail().message("支付参数有误");
    	        
    	    }
    	    
    	} catch (BusinessException e) {
    	    
    	    logger.error("#onWechatpayNotify - An business error occurs when doing callback process.", e);
    	    
    	    callbackResponse.fail().message(e.getMessage());
    	    
    	} catch (Exception e) {
    	    
    	    logger.error("@onWechatpayNotify - An exception error occurs when doing callback process.", e);
    	    
    	    callbackResponse.fail().message("支付回调失败");
    	}

        return callbackResponse.isValid() ? Response.newInstance().success() : Response.newInstance().fail(callbackResponse.getMessage());
    
    }
    
    /**
     * 
     * @param requestParameters
     * @return
     * @throws UnsupportedEncodingException 
     */
    @RequestMapping(value = "/handpay-callback", method = RequestMethod.POST, produces = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
    public @ResponseBody String onHandpayNotify(@RequestBody String body) throws UnsupportedEncodingException {
        
        logger.info("#onHandpayNotify get result [{}]", JsonUtils.toJson(body));
        
        body = URLDecoder.decode(body, StandardCharsets.UTF_8.toString());
        
        Map<String, String> requestParameters = com.demo2do.core.utils.StringUtils.parseQueryString(body);
        
        logger.info("#onHandpayNotify get result [{}]", JsonUtils.toJson(requestParameters));
        
        CallbackRequest callbackRequest = new HandpayCallbackRequest(requestParameters);;
        
        CallbackResponse callbackResponse = new CallbackResponse();
        
        try {
            
            if(callbackRequest.isValid()) {
                
                // 0. extract pay_transaction from request
                PayTransaction payTransaction = callbackRequest.getPayTransaction();
                
                // 1. load payment_context by out_trade_no
                PaymentContext paymentContext =  paymentService.loadContext(payTransaction.getOutTradeNo());
                
                // 2. validate payment_context and callback_request, may throw business exception here
                paymentFacade.validate(paymentContext, callbackRequest);
                
                // 3. do paid process 
                callbackResponse = paymentFacade.paid(paymentContext, payTransaction);
                
            } else {
                
                callbackResponse.fail().message("支付参数有误");
                
            }
            
        } catch (BusinessException e) {
            
            logger.error("#onHandpayNotify - An business error occurs when doing callback process.", e);
            
            callbackResponse.fail().message(e.getMessage());
            
        } catch (Exception e) {
            
            logger.error("#onHandpayNotify - An exception error occurs when doing callback process.", e);
            
            callbackResponse.fail().message("支付回调失败");
        }
        
        String result = callbackResponse.isValid() ? "success" : "fail";
        
        logger.info("start call with result[{}]", result);
        
        return result;
       
    }
    
    /**
     * 
     * @param responseBody
     * @return
     */
    @RequestMapping(value = "/yoppay-callback", method = RequestMethod.POST, produces = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
    public @ResponseBody String onYoppayNotify(@RequestBody String result,
                                               @RequestParam("response") String responseBody,
                                               @RequestParam("customerIdentification") String customerIdentification) {
            
            logger.info("get current result [{}]", result);
            
            logger.info("get yoppay response [{}]", responseBody);
            
            logger.info("get yoppay customerIdentification [{}]", customerIdentification);
            
            CallbackResponse callbackResponse = new CallbackResponse();
        
            
            DigitalEnvelopeDTO dto = new DigitalEnvelopeDTO();
            dto.setCipherText(responseBody);
            PrivateKey privateKey = InternalConfig.getISVPrivateKey(CertTypeEnum.RSA2048);
            logger.info("privateKey: {}", Base64.encode(privateKey.getEncoded()));
            PublicKey publicKey = InternalConfig.getYopPublicKey(CertTypeEnum.RSA2048);
            logger.info("publicKey: {}", Base64.encode(publicKey.getEncoded()));
            
            Map<String,Object> jsonMap  = new HashMap<>();
            dto = DigitalEnvelopeUtils.decrypt(dto, privateKey, publicKey);
            logger.info("get result [{}]", dto.getPlainText());
            
            jsonMap = JsonUtils.parse(dto.getPlainText());
            logger.info("get json result [{}]", jsonMap);
            
            String status = jsonMap.get("status") == null ? null : jsonMap.get("status").toString();
            String uniqueOrderNo = jsonMap.get("externalNo") == null ? null : jsonMap.get("externalNo").toString();
            String orderId = jsonMap.get("customerRequestNo") == null ? null : jsonMap.get("customerRequestNo").toString();
            
            if(StringUtils.isNotEmpty(status) && StringUtils.equalsIgnoreCase(status, "SUCCESS") && StringUtils.isNotEmpty(uniqueOrderNo) && StringUtils.isNotBlank(orderId)) {
                
                // 1. load payment_context by out_trade_no
                PaymentContext paymentContext =  paymentService.loadContext(orderId);
                
                double amountdb = jsonMap.get("amount") == null ? null : Double.parseDouble(jsonMap.get("amount").toString());
                String amount = new BigDecimal(amountdb).multiply(new BigDecimal(100)).setScale(0, BigDecimal.ROUND_HALF_UP).toString();
                String paymentTime = DateFormatUtils.format(new Date(), "yyyyMMddHHmmss");
                PayTransaction payTransaction = new CallbackContext(orderId, uniqueOrderNo, amount, paymentTime);
                
                // 3. do paid process 
                callbackResponse = paymentFacade.paid(paymentContext, payTransaction);
                
                return callbackResponse.isValid() ? "success" : "fail";
                
            } else {
                return "fail";
            }
    }
    
    /**
     * 
     * @param httpServletRequest
     * @return
     */
    @SuppressWarnings("rawtypes")
    @RequestMapping(value = "/alipay-callback", method = RequestMethod.POST, produces = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
    public @ResponseBody String alipayCallback(HttpServletRequest httpServletRequest) {
        
        CallbackResponse callbackResponse = new CallbackResponse();
        
        String url = WebUtils.getRealRequestURL(httpServletRequest);
        logger.info("current url is [{}]", url);
        
        Map<String,String> params = new HashMap<String,String>();
        Map requestParams = httpServletRequest.getParameterMap();
        for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i]
                        : valueStr + values[i] + ",";
            }
            //乱码解决，这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
            //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk");
            params.put(name, valueStr);
        }
        
        logger.info("get current result [{}]", JsonUtils.toJson(params));
        try {
            
            // 1. Get basic trade info
            String outTradeNo = params.get("out_trade_no");
            String trade_no = params.get("trade_no");
            String trade_status = params.get("trade_status");
            String gmt_payment = params.get("gmt_payment");
            String amount = params.get("total_amount");
            
            // 2. Make payTransaction
            BigDecimal bigDecimal = new BigDecimal(amount).multiply(new BigDecimal(100));
            String amountString = String.valueOf(bigDecimal.intValue());
            PayTransaction payTransaction = new CallbackContext(outTradeNo, trade_no, amountString, gmt_payment);
            
            if(StringUtils.isNotBlank(payTransaction.getTransactionCode()) &&  StringUtils.isNotBlank(payTransaction.getOutTradeNo())) {
                
                // 1. load payment_context by out_trade_no
                PaymentContext paymentContext =  paymentService.loadContext(payTransaction.getOutTradeNo());
                
                if(paymentContext != null) {
                    
                    // 2. validate by alipay sdk
                    boolean verify_result = AlipaySignature.rsaCheckV1(params, alipayPublicKey, "UTF-8", "RSA2");
                    
                    if(verify_result){//验证成功
                        if(trade_status.equalsIgnoreCase("TRADE_FINISHED")) {
                            return "success";
                        } else if (trade_status.equals("TRADE_SUCCESS")){
                            
                            String seller_id = params.get("seller_id");
                            if(alipaySellerId.equalsIgnoreCase(seller_id) && amountString.equalsIgnoreCase(paymentContext.getAmountAsString())) {
                                callbackResponse = paymentFacade.paid(paymentContext, payTransaction);
                                return callbackResponse.isValid() ? "success" : "faild";
                            } else {
                                logger.error("validate seller id & amount faild");
                                return "fail";
                            }
                        }
                    } else {
                        logger.error("validate sign error");
                        return "fail";
                    }
                }
            }
            
            
        } catch (Exception e) {
            logger.error("alipay callback error", e);
        }
        
        return "fail";

        
    }
}
